package ft.jsf.impl;

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.LinkedHashMap;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

import ft.jsf.cnst.C;
import ft.jsf.util.U;

/**
 * 去掉 js 文件的注释与回车<br />
 * 业务实现
 */
public class JsfImpl
{
	
	/**
	 * 输入文件路径
	 */
	private String _src_path;
	
	/**
	 * 输入文件路径
	 * @return
	 */
	public String get_src_path()
	{
		return _src_path;
	}
	
	/**
	 * 输入文件路径
	 * @param _src_path
	 */
	public void set_src_path(String _src_path)
	{
		this._src_path = _src_path;
	}
	
	/**
	 * 输出文件路径
	 */
	private String _fix_path;

	/**
	 * 输出文件路径
	 * @return
	 */
	public String get_fix_path()
	{
		return _fix_path;
	}
	
	/**
	 * 输出文件路径
	 * @param _fix_path
	 */
	public void set_fix_path(String _fix_path)
	{
		this._fix_path = _fix_path;
	}
	
	/**
	 * 如果要读文件，则应该使用什么文字编码格式？
	 */
	private String _encoding;
	
	/**
	 * 文件读写-文字编码格式
	 * @return
	 */
	public String get_encoding()
	{
		return _encoding;
	}
	
	/**
	 * 文件读写-文字编码格式
	 * @param _encoding
	 */
	public void set_encoding(String _encoding)
	{
		this._encoding = _encoding;
	}
	
	/**
	 * 输入源代码
	 */
	private String _src_str;
	
	/**
	 * 输入源代码
	 * @return
	 */
	public String get_src_str()
	{
		return _src_str;
	}
	
	/**
	 * 输入源代码
	 * @param _src_str
	 */
	public void set_src_str(String _src_str)
	{
		this._src_str = _src_str;
	}
	
	/**
	 * 输出源代码
	 */
	private String _fix_str;
	
	/**
	 * 输出源代码
	 * @return
	 */
	public String get_fix_str()
	{
		return _fix_str;
	}
	
	/**
	 * 输出源代码
	 * @param _fix_str
	 */
	public void set_fix_str(String _fix_str)
	{
		this._fix_str = _fix_str;
	}
	
	/**
	 * 输出日志消息
	 */
	private StringBuffer _msg_out = new StringBuffer( 512 );
	
	public String getMsgOut()
	{
		return _msg_out.toString();
	}
	
	private String putMsgOut(String str)
	{
		if(str!=null && str.length()>0)
		{
			if(null == _msg_out)
			{
				_msg_out = new StringBuffer( 256 );
			}
			_msg_out.append(str);
			_msg_out.append('\n');
		}
		return _msg_out.toString();
	}
	
	/**
	 * 业务逻辑执行
	 * @throws Exception
	 */
	public void startRun()
			throws Exception
	{
		String str = null;
		
		String my_encoding;
		if(_encoding!=null && _encoding.length()>0)
		{
			my_encoding = _encoding;
		}else
		{
			my_encoding = C.DEFAULT_ENCODING;
		}
		
		if(_src_path!=null && _src_path.length()>0)
		{
			// 从文件读入
			FileInputStream fis = new FileInputStream(_src_path);
			str = U.getStrFromIs(fis, my_encoding);
			fis.close();
			
			if(str!=null && str.length()>0)
			{
				_src_str = str;
			}
			
			/// DEBUG ///
//			System.out.print( "修改前 str.length = " );
//			System.out.println( str.length() );
			putMsgOut("修改前 str.length = " + Integer.toString(str.length()) );
			/// DEBUG ///
		}
		else
		{
			// 否则判断是否从 _src_str 字段读入
			if(_src_str!=null && _src_str.length()>0)
			{
				str = _src_str;
			}
		}
		
		String fix = null;
		
		if(str!=null && str.length()>0)
		{
			fix = getFixString(str);
			
			
			
			/// DEBUG ///
//			System.out.print( "修改后 fix.length = " );
//			System.out.println( fix.length() );
			putMsgOut("修改后 fix.length = " + Integer.toString(fix.length()) );
			/// DEBUG ///
			
		}
		
		if(fix!=null && fix.length()>0)
		{
			if(_fix_path!=null && _fix_path.length()>0)
			{
				// 文件输出
				FileOutputStream fos = new FileOutputStream(_fix_path);
				U.putStrToOs(fix, fos, my_encoding);
				fos.close();
			}
			
			// 其实，这里无论是否输出至文件，都需要输出至字段
			_fix_str = fix;
		}
	}
	
	/**
	 * 修正字符串，替换其中的制表符，注释，回车等东西。
	 * @param str
	 * @return
	 */
	private static String getFixString(String str)
			throws Exception
	{
		String ret = "";
		
		if(str!=null && str.length()>0)
		{
			int lns = str.length();
			
			// 刚刚过去的字符，是否处在注释中？如果该值为 true 则就算当前是正常字符，也应该直接忽略
			// 0 正常 1 注释 2 文本
			int lastSkipType = 0;
			// 注释类型，1 行注释，0 块注释
			int skipTypeLine = 0;
			
			
			// 处于字符串中，类型 // 1 双引号 0 单引号
			int textDoubleType  = 0;
			
			StringBuffer strn = new StringBuffer( lns );
			
			for(int i=0; i<lns; i++)
			{
				// 当前字符
				char currChar = str.charAt(i);
				char lastChar = (i>0) ? ( str.charAt( i - 1 ) ) : ('\0') ;
				char nextChar = ( (i+1) <lns) ? ( str.charAt( 1+i ) ) : ('\0') ;
				
				if(lastSkipType == 1)
				{
					// 在注释中 // 则应该忽略所有字符
					// 除非再次遇到注释结束的标志
					// 注释有两种，行注释 "//" 一直到行结尾，也就是遇到回车就表示终结
					// 块注释一直要遇到 */ 才表示结束
					// 所以这两种注释的结束标志不可以混用
					if(skipTypeLine == 1)
					{
						// 如果是行注释，则需要判断是否一直到换行
						if('\r'==currChar || '\n' == currChar)
						{
							
							// 设置不再处于注释中
							lastSkipType = 0;
							
							continue;
						}
					}
					else if(skipTypeLine == 0)
					{
						// 如果是块注释
						if('/' == currChar && '*' == lastChar)
						{
							lastSkipType = 0;
							continue;
						}
					}
				}
				else if(lastSkipType == 0)
				{
					// 不在注释中
					
					// 替换回车
					if('\r' == currChar || '\n' == currChar || '\t' == currChar)
					{
						// do nothing 直接走下一次循环
						continue;
					}
					else if('/' == str.charAt(i))
					{
						// 判断注释
						if( '*' == nextChar )
						{
							// 进入块注释
							lastSkipType = 1;
							skipTypeLine = 0;
							continue;
						}
						else if( '/' == nextChar)
						{
							// 进入行注释
							lastSkipType = 1;
							skipTypeLine = 1;
							continue;
						}
						else
						{
							// 其他任意字符都直接输出
							strn.append( currChar );
						}
					}
					else if('\'' == currChar)
					{
						lastSkipType = 2;
						textDoubleType = 0;
						
						// 如果检测到进入单引号
						// 仍然需要输出该字符
						
						strn.append( currChar );
						continue;
					}
					else if('"' == currChar)
					{
						lastSkipType = 2;
						textDoubleType = 1;
						
						// 如果检测到进入双引号
						// 仍然需要输出该字符
						
						strn.append( currChar );
						continue;
					}
					else
					{
						// 其他字符普通输出字符即可
						strn.append( currChar );
					}
					
				}
				else if(lastSkipType == 2)
				{
					// 如果处于字符串中 则应该忽略注释
					
					if('\'' == currChar && 0 == textDoubleType && '\\' != lastChar )
					{
						// 如果当前处于字符串中，且刚好遇到了当前正在经过的字符串符号，且该字符的前面没有转义符号
						// 则应该结束当前处于字符串中的状态
						
						lastSkipType = 0;
						
						// 同时当前字符也应该正常输出
						strn.append( currChar );
						continue;
					}
					else if('"' == currChar && 1 == textDoubleType && '\\' != lastChar )
					{
						lastSkipType = 0;
						strn.append( currChar );
						continue;
					}
					else
					{
						// 其他情况照常输出字符
						strn.append(currChar);
						continue;
					}
					
				}
				else
				{
					/// DEBUG ///
					String err_msg = "lastSkipType 状态异常！";
					System.out.println(err_msg);
					throw new Exception(err_msg);
				}
				
				
				
			}
			
			if(strn!=null && strn.length()>0)
			{
				ret = strn.toString();
			}
		}
		
		
		return ret;
	}
}
